package com.example.zuul.filter;

import cn.hutool.core.util.StrUtil;
import com.example.zuul.feign.SsoFeign;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Map;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

public class AccessFilter extends ZuulFilter {
    Logger logger = LoggerFactory.getLogger(AccessFilter.class);

    @Autowired
    private SsoFeign ssoFeign;

    /*
     * shouldFilter 判断该过滤器是否需要被执行
     *
     * 这里直接返回true，表示该过滤器对所有请求都会生效。
     * 实际运用中我们可以利用该函数指定过滤器的有效范围
     */
    @Override
    public boolean shouldFilter() {
        System.out.println("-----------生效范围----------");
        return true;
    }

    /*
     * 过滤器的具体逻辑
     *
     * 这里我们通过ctx.setSendZuulResponse(false)让zuul过来请求，不对其进行路由
     * 然后通过ctx.setResponseStatusCode(401)设置了返回的错误码
     *
     */
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();

        //访问路径
        StringBuilder url = new StringBuilder(request.getRequestURL().toString());

        //从cookie里面取值（Zuul丢失Cookie的解决方案：https://blog.csdn.net/lindan1984/article/details/79308396）
        String accessToken = request.getParameter("accessToken");
        Cookie[] cookies = request.getCookies();
        if(null != cookies){
            for (Cookie cookie : cookies) {
                if ("accessToken".equals(cookie.getName())) {
                    accessToken = cookie.getValue();
                }
            }
        }
        //过滤规则：cookie有令牌且存在于Redis，或者访问的是登录页面、登录请求则放行
        if (url.toString().contains("product-service/sso/loginPage") || url.toString().contains("product-service/sso/login") || (!StrUtil.isEmpty(accessToken) && ssoFeign.hasKey(accessToken))) {
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            return null;
        } else {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);

            //如果是get请求处理参数，其他请求统统跳转到首页
            String method = request.getMethod();
            if("GET".equals(method)){
                url.append("?");
                Map<String, String[]> parameterMap = request.getParameterMap();
                Object[] keys = parameterMap.keySet().toArray();
                for (int i = 0; i < keys.length; i++) {
                    String key = (String) keys[i];
                    String value = parameterMap.get(key)[0];
                    url.append(key).append("=").append(value).append("&");
                }
                //处理末尾的&符合
                url.delete(url.length() -1,url.length());
            }else{
                //首页链接，或者其他固定页面
                url =  new StringBuilder("http://localhost:9200/product-service/sso/main");
            }

            //重定向到登录页面
            try {
                response.sendRedirect("http://localhost:9200/product-service/sso/loginPage?url=" + url);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    /* filterType 返回过滤器类型
     * 他决定了过滤器在请求的哪个生命周期中执行。这里定义为pre，代表会在请求被路由前执行。
     *
     * pre:请求执行之前filter
     * route: 处理请求，进行路由
     * post: 请求处理完成后执行的filter
     * error:出现错误时执行的filter
     */
    @Override
    public String filterType() {
        System.out.println("-----------请求执行之前----------");
        return PRE_TYPE;
    }


    /*
     * filterOrder 返回过滤器的执行顺序
     *
     * 当请求在一个阶段有多个过滤器是，需要根据该方法的返回值来依次执行
     *
     */
    @Override
    public int filterOrder() {
        // PreDecoration之前运行
        return PRE_DECORATION_FILTER_ORDER - 1;
    }
}
